package org.owasp.webgoat.session;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.LinkedList;

import javax.servlet.ServletContext;

import org.owasp.webgoat.HammerHead;
import org.owasp.webgoat.lessons.AbstractLesson;
import org.owasp.webgoat.lessons.Category;

/*******************************************************************************
 * 
 * 
 * This file is part of WebGoat, an Open Web Application Security Project
 * utility. For details, please see http://www.owasp.org/
 * 
 * Copyright (c) 2002 - 2007 Bruce Mayhew
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 * 
 * Getting Source ==============
 * 
 * Source for this application is maintained at code.google.com, a repository
 * for free software projects.
 * 
 * For details, please see http://code.google.com/p/webgoat/
 *
 * @author     Bruce Mayhew <a href="http://code.google.com/p/webgoat">WebGoat</a>
 * @created    October 28, 2003
 */
public class Course
{

    private List<AbstractLesson> lessons = new LinkedList<AbstractLesson>();

    private final static String PROPERTIES_FILENAME = HammerHead.propertiesPath;

    private WebgoatProperties properties = null;
    
    private List<String> files = new LinkedList<String>();


    public Course()
    {
		try
		{
		    properties = new WebgoatProperties(PROPERTIES_FILENAME);
		}
		catch (IOException e)
		{
		    System.out.println("Error loading WebGoat properties");
		    e.printStackTrace();
		}
    }
    
    /**
     * Take an absolute file and return the filename.
     * 
     * Ex. /etc/password becomes password
     * 
     * @param s
     * @return the file name
     */
    private static String getFileName(String s)
    {
    	String fileName = new File(s).getName();
		
		if(fileName.indexOf("/") != -1)
		{
			fileName = fileName.substring(fileName.lastIndexOf("/"), fileName.length());
		}
		
		if(fileName.indexOf(".") != -1)
		{
			fileName = fileName.substring(0, fileName.indexOf("."));
		}
		
		return fileName;
    }
    
    /**
     * Take a class name and return the equivalent file name
     * 
     * Ex. org.owasp.webgoat becomes org/owasp/webgoat.java
     * 
     * @param className
     * @return
     */
    private static String getSourceFile(String className)
    {
    	StringBuffer sb = new StringBuffer();
    	
    	sb.append(className.replace(".", "/"));
    	sb.append(".java");
    	
    	return sb.toString();
    }


    /**
     *  Takes a file name and builds the class file name
     *
     * @param  fileName  Description of the Parameter
     * @param  path      Description of the Parameter
     * @return           Description of the Return Value
     */
    private static String getClassFile(String fileName, String path)
    {
    	String ext = ".class";
		fileName = fileName.trim();
		
		/**
		 * We do not handle directories.
		 * We do not handle files with different extensions
		 */
		if(fileName.endsWith("/") || !fileName.endsWith(ext))
		{
			return null;
		}
	
		// if the file is in /WEB-INF/classes strip the dir info off
		int index = fileName.indexOf("/WEB-INF/classes/");
		if (index != -1)
		{
		    fileName = fileName.substring(index + "/WEB-INF/classes/".length(), fileName.length() - ext.length());
		    fileName = fileName.replace('/', '.');
		    fileName = fileName.replace('\\', '.');
		}
		else
		{
		    // Strip off the leading path info
		    fileName = fileName.substring(path.length(), fileName.length() - ext.length());
		}
	
		return fileName;
    }

    /**
     *  Gets the categories attribute of the Course object
     *
     * @return    The categories value
     */
    public List getCategories()
    {
	List<Category> categories = new ArrayList<Category>();
	Iterator iter = lessons.iterator();

	while (iter.hasNext())
	{
	    AbstractLesson lesson = (AbstractLesson) iter.next();

	    if (!categories.contains(lesson.getCategory()))
	    {
		categories.add(lesson.getCategory());
	    }
	}

	Collections.sort(categories);

	return categories;
    }


    /**
     *  Gets the firstLesson attribute of the Course object
     *
     * @return    The firstLesson value
     */
    public AbstractLesson getFirstLesson()
    {
	List<String> roles = new ArrayList<String>();
	roles.add(AbstractLesson.USER_ROLE);
	// Category 0 is the admin function.  We want the first real category 
	// to be returned. This is noramally the General category and the Http Basics lesson
	return ((AbstractLesson) getLessons((Category) getCategories().get(1),
		roles).get(0));
    }


    /**
     *  Gets the lesson attribute of the Course object
     *
     * @param  lessonId  Description of the Parameter
     * @param  role      Description of the Parameter
     * @return           The lesson value
     */
    public AbstractLesson getLesson(WebSession s, int lessonId, List roles)
    {
	if (s.isHackedAdmin())
	{
	    roles.add(AbstractLesson.HACKED_ADMIN_ROLE);
	}
	//System.out.println("getLesson() with roles: " + roles);
	Iterator iter = lessons.iterator();

	while (iter.hasNext())
	{
	    AbstractLesson lesson = (AbstractLesson) iter.next();

	    //System.out.println("getLesson() at role: " + lesson.getRole());
	    if (lesson.getScreenId() == lessonId
		    && roles.contains(lesson.getRole()))
	    {
		return lesson;
	    }
	}

	return null;
    }


    public AbstractLesson getLesson(WebSession s, int lessonId, String role)
    {
	List<String> roles = new Vector<String>();
	roles.add(role);
	return getLesson(s, lessonId, roles);
    }


    public List getLessons(WebSession s, String role)
    {
	List<String> roles = new Vector<String>();
	roles.add(role);
	return getLessons(s, roles);
    }


    /**
     *  Gets the lessons attribute of the Course object
     *
     * @param  role  Description of the Parameter
     * @return       The lessons value
     */
    public List getLessons(WebSession s, List<String> roles)
    {
	if (s.isHackedAdmin())
	{
	    roles.add(AbstractLesson.HACKED_ADMIN_ROLE);
	}
	List<String> lessonList = new ArrayList<String>();
	Iterator categoryIter = getCategories().iterator();

	while (categoryIter.hasNext())
	{
	    lessonList.addAll(getLessons(s, (Category) categoryIter.next(),
		    roles));
	}
	return lessonList;
    }


    /**
     *  Gets the lessons attribute of the Course object
     *
     * @param  category  Description of the Parameter
     * @param  role      Description of the Parameter
     * @return           The lessons value
     */
    private List getLessons(Category category, List roles)
    {
	List<AbstractLesson> lessonList = new ArrayList<AbstractLesson>();

	Iterator iter = lessons.iterator();
	while (iter.hasNext())
	{
	    AbstractLesson lesson = (AbstractLesson) iter.next();

	    if (lesson.getCategory().equals(category)
		    && roles.contains(lesson.getRole()))
	    {
		lessonList.add(lesson);
	    }
	}

	Collections.sort(lessonList);
	//		System.out.println(java.util.Arrays.asList(lessonList));
	return lessonList;
    }


    public List getLessons(WebSession s, Category category, String role)
    {
	List<String> roles = new Vector<String>();
	roles.add(role);
	return getLessons(s, category, roles);
    }


    public List getLessons(WebSession s, Category category, List<String> roles)
    {
	if (s.isHackedAdmin())
	{
	    roles.add(AbstractLesson.HACKED_ADMIN_ROLE);
	}
	return getLessons(category, roles);
    }
    
    /**
     * Load all of the filenames into a temporary cache
     * 
     * @param context
     * @param path
     */
    private void loadFiles(ServletContext context, String path)
    {
    	Set resourcePaths = context.getResourcePaths(path);
    	Iterator itr = resourcePaths.iterator();
    	
    	while(itr.hasNext())
    	{
    		String file = (String)itr.next();
    		
    		if(file.length() != 1 && file.endsWith("/"))
    		{
    			loadFiles(context, file);
    		}
    		else
    		{	
    			files.add(file);
    		}
    	}
    }
    
    /**
     * Instantiate all the lesson objects into a cache
     * 
     * @param path
     */
    private void loadLessons(String path)
    {
    	Iterator itr = files.iterator();
    	
    	while(itr.hasNext())
    	{
    		String file = (String)itr.next();
    		String className = getClassFile(file, path);
    		
    		if(className != null && !className.endsWith("_i"))
    		{
    			try
    			{
    				Class c = Class.forName(className);
    				Object o = c.newInstance();
    				
    				if(o instanceof AbstractLesson)
    				{
    					AbstractLesson lesson = (AbstractLesson)o;
    					
    					lesson.update(properties);
    					
    					if(lesson.getHidden() == false)
    					{
    						lessons.add(lesson);
    					}
    				}
    			}
    			catch (Exception e)
    			{
    				//System.out.println("Warning: " + e.getMessage());
    			}
    		}
    	}
    }
    
    /**
     * For each lesson, set the source file and lesson file
     */
    private void loadResources()
    {
    	Iterator lessonItr = lessons.iterator();
    	
    	while(lessonItr.hasNext())
    	{
    		AbstractLesson lesson = (AbstractLesson)lessonItr.next();
    		String className = lesson.getClass().getName();
    		String classFile = getSourceFile(className);
    		
    		Iterator fileItr = files.iterator();
    		
    		while(fileItr.hasNext())
    		{
    			String absoluteFile = (String)fileItr.next();
    			String fileName = getFileName(absoluteFile);
    			
    			if(absoluteFile.endsWith(classFile))
    			{
    				//System.out.println("Set source file for " + classFile);
    				lesson.setSourceFileName(absoluteFile);
    			}
    			
    			if(absoluteFile.endsWith(".html") && className.endsWith(fileName))
    			{
    				//System.out.println("DEBUG: setting lesson plan file " + absoluteFile + " for lesson " + lesson.getClass().getName());
    				lesson.setLessonPlanFileName(absoluteFile);
    			}
    		}
    	}
    }

    /**
     *  Description of the Method
     *
     * @param  path     Description of the Parameter
     * @param  context  Description of the Parameter
     */
    public void loadCourses(boolean enterprise, ServletContext context, String path)
    {
    	loadFiles(context, path);
    	loadLessons(path);
    	loadResources();
    }
}
